#include "OperatorFunctionBuilder.h"
#include "ScriptCompiler/ScriptFileContext.h"

#include "ScriptCompiler/AST/OperatorFunctionNode.h"
#include "ScriptCompiler/AST/DecoratorNode.h"

#include "ScriptCompiler/ASTBuilder/FunctionParameterBuilder.h"
#include "ScriptCompiler/ASTBuilder/SentenceBlockBuilder.h"
#include "ScriptCompiler/ASTBuilder/TypeBuilder.h"
#include "ScriptCompiler/ASTBuilder/ExpressionBuilder.h"


COperatorFunctionBuilder::COperatorFunctionBuilder(std::shared_ptr<CScriptFileContext> InContext)
{
    Context = InContext;
    Result = std::make_shared<COperatorFunctionNode>(Context);
}

bool COperatorFunctionBuilder::ReadCurentToken()
{

    {
        auto OperatorKeyword = Context->GetCurrentToken();

        if (OperatorKeyword->Type != EScriptTokenType::Operator)
        {
            Context->RaiseError(10073,OperatorKeyword,U"Expected operator");
            return false;
        }
        
        Result->OperatorKeyword = OperatorKeyword;
        Context->CurrentTokenIndex++;
    } 

    {
        //check operator type
        auto OperatorTypeToken = Context->GetCurrentToken();
        if (OperatorTypeToken->Type  >= EScriptTokenType::MinOverloadOperator
            && OperatorTypeToken->Type <= EScriptTokenType::MaxOverloadOperator)
        {
        }
        else if(OperatorTypeToken->Type==EScriptTokenType::Identifier)
        {
            // this is a type case operator
            
        }
        else
        {
            Context->RaiseError(10074,OperatorTypeToken,U"Expected operator type");
            return false;
        }

        Result->Operator = OperatorTypeToken;
        Context->CurrentTokenIndex++;
    }


    {
        auto LeftParenthesesToken = Context->GetCurrentToken();
        if (LeftParenthesesToken->Type != EScriptTokenType::LeftParentheses)
        {
            Context->RaiseError(10075,LeftParenthesesToken,U"Expected left parenthesis");
            return false;
        }

        Context->CurrentTokenIndex++;
    }

    {

        auto RightParenthesesToken = Context->GetCurrentToken();
        while(RightParenthesesToken->Type != EScriptTokenType::RightParentheses)
        {
            auto ParameterBuilder = std::make_shared<CFunctionParameterBuilder>(Context);
            if (!ParameterBuilder->ReadCurentToken())
            {
                return false;
            }

            Result->Parameters.Add(ParameterBuilder->Result);

            auto CommaToken = Context->GetCurrentToken();
            if (CommaToken->Type == EScriptTokenType::Comma)
            {
                Context->CurrentTokenIndex++; 
            }
            else if (CommaToken->Type == EScriptTokenType::RightParentheses)
            {
                break;
            }
            else
            {
                Context->RaiseError(10076,CommaToken,U"Expected comma or right parenthesis after parameter");
                return false;
            }

            RightParenthesesToken = Context->GetCurrentToken();
        }

        //in case empty parameter name
        auto CommaToken = Context->GetFormerToken();
        if(CommaToken->Type == EScriptTokenType::Comma)
        {
            Context->RaiseError(10077,CommaToken,U"Expected parameter after comma");
            return false;
        }

        Context->CurrentTokenIndex++; //skip right parenthesis
    }

    {
        auto LambdaToken = Context->GetCurrentToken();

        switch (LambdaToken->Type)
        {
            case EScriptTokenType::LeftBrace:
            {
                Context->CurrentTokenIndex++; //skip left brace

                auto RightBraceIndex = FindPairedBrace(Context);
                if (!RightBraceIndex.HasValue())
                {
                    //raise error, require '}'
                    Context->RaiseError(10165,Context->GetCurrentToken(),U"Expected paired '}' ");
                    return false;
                }

                auto BlockBuilder = std::make_shared<CSentenceBlockBuilder>(Context,RightBraceIndex.GetValue());
                Result->Content = BlockBuilder->Result;
                if (!BlockBuilder->ReadCurentToken())
                {
                    return false;
                }

                //skip }
                assert(Context->CurrentTokenIndex==RightBraceIndex.GetValue());
                Context->CurrentTokenIndex++;

                return true;
            }

            case EScriptTokenType::Semicolon:
            {
                Context->CurrentTokenIndex++; //skip semicolon
                return true ; // function do not have content ,just a declaration
            }

            case EScriptTokenType::LambdaOperator:
            {
                Context->CurrentTokenIndex++; //skip lambda operator
                
                //find ';'
                auto SemiColonIndex = FindNextToken(EScriptTokenType::Semicolon,Context);
                if(!SemiColonIndex.HasValue())
                {
                    Context->RaiseError(10078,Context->GetCurrentToken(),U"Expected ';' at end of lambda function");
                    return false;
                }

                auto ExpressionBuilder = std::make_shared<CExpressionBuilder>(Context,SemiColonIndex.GetValue());

                if (!ExpressionBuilder->ReadCurentToken())
                {
                    return false;
                }

                return true;
            }

            default:
            {
                Context->RaiseError(10079,LambdaToken,U"Expected ';' or '{' or '=>' after function parameters");
                return false;
            }
        }



    }



}
